/* Copyright (c) 2021-2025 Qualcomm Technologies International, Ltd. */
/* %%version */

#include "gatt_vcs_client.h"
#include "gatt_vocs_client.h"
#include "gatt_aics_client.h"

#include "vcp.h"
#include "vcp_debug.h"
#include "vcp_init.h"
#include "vcp_common.h"
#include "gatt_service_discovery_lib.h"

vcp_main_inst *vcp_main;

void vcpFreeInstanceData(VCP *vcp_inst)
{
    ServiceHandle clntHndl = vcp_inst->vcp_srvc_hdl;

    if(FREE_VCP_CLIENT_INST(vcp_inst->vcp_srvc_hdl))
    {
        VCP_REMOVE_SERVICE_HANDLE(vcp_main->profile_handle_list, clntHndl);
    }
    else
    {
        VCP_INFO("vcpFreeInstanceData:Freeing of memory instance failed\n");
    }
}

static void vcpVcsInitReq(VCP *vcp_inst,
                          VcpInitData *client_init_params,
                          VcpHandles *device_data)
{
    GattVcsClientInitData vcs_init_data;
    GattVcsClientDeviceData vcs_device_data;

    vcs_init_data.cid = client_init_params->cid;
    vcs_init_data.startHandle = device_data->vcsHandle.startHandle;
    vcs_init_data.endHandle = device_data->vcsHandle.endHandle;

    vcs_device_data.volumeStateHandle = device_data->vcsHandle.volumeStateHandle;
    vcs_device_data.volumeStateCccHandle = device_data->vcsHandle.volumeStateCccHandle;
    vcs_device_data.volumeFlagsHandle = device_data->vcsHandle.volumeFlagsHandle;
    vcs_device_data.volumeFlagsCccHandle = device_data->vcsHandle.volumeFlagsCccHandle;
    vcs_device_data.volumeControlPointHandle = device_data->vcsHandle.volumeControlPointHandle;

    GattVcsClientInitReq(vcp_inst->lib_task, &vcs_init_data, &vcs_device_data);
}

static void InitProfileHandleList(CsrCmnListElm_t *elem)
{
    /* Initialize a CsrBtAseCharacElement. This function is called every
     * time a new entry is made on the queue list */
    ProfileHandleListElm_t *cElem = (ProfileHandleListElm_t *) elem;

    cElem->profile_handle = 0;
}

#ifndef EXCLUDE_CSR_BT_VOCS_CLIENT_MODULE
static CsrBool setVocsServiceHandle(CsrCmnListElm_t *element, void *value)
{
    vcpVocsServiceQueueElement *incElement = (vcpVocsServiceQueueElement *)element;
    const GattVocsClientInitCfm *message = (GattVocsClientInitCfm *)value;

    if (incElement && incElement->service_handle == INVALID_PROFILE_HANDLE)
    {
        incElement->service_handle = message->srvcHndl;
        return TRUE;
    }
    return FALSE;
}
#endif

#ifndef EXCLUDE_CSR_BT_AICS_CLIENT_MODULE
static CsrBool setAicsServiceHandle(CsrCmnListElm_t *element, void *value)
{
    vcpAicsServiceQueueElement *incElement = (vcpAicsServiceQueueElement*)element;
    const GattAicsClientInitCfm *message = (GattAicsClientInitCfm *)value;

    if (incElement && incElement->service_handle == INVALID_PROFILE_HANDLE)
    {
         incElement->service_handle = message->srvcHndl;
         return TRUE;
    }
    return FALSE;
}
#endif

static void initiateIncService(CsrCmnListElm_t *element, void *value)
{
    vcpIncOptService incService = *(vcpIncOptService*)value;
    ProfileHandleListElm_t* elem = NULL;
    VCP* vcp_inst = NULL;
    vcp_main = vcpGetMainInstance();

    if (incService == VCP_INCLUDE_VOCS)
    {
        vcpVocsServiceQueueElement* incElement = (vcpVocsServiceQueueElement*)element;
        GattVocsClientInitData init_data;

        if (incElement)
        {
            init_data.cid = incElement->cid;
            init_data.startHandle = incElement->device_data.startHandle;
            init_data.endHandle = incElement->device_data.endHandle;

            elem = VCP_FIND_PROFILE_HANDLE_BY_VOCS_SERVICE_HANDLE(vcp_main->profile_handle_list,
                incElement->service_handle);

            if (elem)
            {
                vcp_inst = FIND_VCP_INST_BY_PROFILE_HANDLE(elem->profile_handle);
            }

            if (vcp_inst && vcp_inst->is_peer_device)
            {
                GattVocsClientInitReq(vcp_inst->lib_task,
                    &init_data,
                    &(incElement->device_data));
            }
            else if (vcp_inst)
            {
                GattVocsClientInitReq(vcp_inst->lib_task,
                    &init_data,
                    NULL);
            }
        }
    }
    else if (incService == VCP_INCLUDE_AICS)
    {
        vcpAicsServiceQueueElement *incElement = (vcpAicsServiceQueueElement*)element;

        if (incElement)
        {
            GattAicsClientInitData init_data;

            init_data.cid = incElement->cid;
            init_data.startHandle = incElement->device_data.startHandle;
            init_data.endHandle = incElement->device_data.endHandle;

            elem = VCP_FIND_PROFILE_HANDLE_BY_AICS_SERVICE_HANDLE(vcp_main->profile_handle_list,
                incElement->service_handle);

            if (elem)
            {
                vcp_inst = FIND_VCP_INST_BY_PROFILE_HANDLE(elem->profile_handle);
            }

            if (vcp_inst&& vcp_inst->is_peer_device)
            {
                GattAicsClientInitReq(vcp_inst->lib_task,
                    &init_data,
                    &(incElement->device_data));
            }
            else if (vcp_inst)
            {
                GattAicsClientInitReq(vcp_inst->lib_task,
                    &init_data,
                    NULL);
            }
        }
    }
}

/***************************STATIC_FUNCTION_END*************************************************/

/***********************************************************************************************/
void vcpSendInitCfm(VCP * vcp_inst, VcpStatus status)
{
    if(vcp_inst)
    {
        MAKE_VCP_MESSAGE(VcpInitCfm);

        message->id = VCP_INIT_CFM;
        message->status = status;
        message->prflHndl = vcp_inst->vcp_srvc_hdl;
        message->vocsNum = vcp_inst->vocs_counter;
        message->aicsNum = vcp_inst->aics_counter;
        message->cid = vcp_inst->cid;

        VcpMessageSend(vcp_inst->app_task, message);
    }
    else
    {
        VCP_DEBUG("Invalid VCP Profile instance\n");
    }
}

void vcpSendInitOptServiceCfm(VCP * vcp_inst, VcpStatus status)
{
    if(vcp_inst)
    {
        MAKE_VCP_MESSAGE(VcpInitOptServiceCfm);

        message->id = VCP_INIT_OPTIONAL_SERVICE_CFM;
        message->status = status;
        message->prflHndl = vcp_inst->vcp_srvc_hdl;
        message->optSerFound = vcp_inst->opt_service;

        /* Update the Included service counter to the original value for future operation tracking e.g.
         * handles read and other include service operations */
        vcp_inst->vocs_counter = vcp_inst->aics_service_queue.count;
        vcp_inst->aics_counter = vcp_inst->aics_service_queue.count;

        VcpMessageSend(vcp_inst->app_task, message);
    }
    else
    {
        VCP_DEBUG("Invalid VCP Profile instance\n");
    }
}

void vcp_init(void **gash)
{
    vcp_main = CsrPmemZalloc(sizeof(*vcp_main));
    *gash = vcp_main;

    CsrCmnListInit(&vcp_main->profile_handle_list, 0, InitProfileHandleList, NULL);
}

/***************************************************************************/
void VcpInitReq(AppTask appTask,
                VcpInitData *clientInitParams,
                VcpHandles *deviceData,
                bool includedServices)
{
    VCP *vcp_inst = NULL;
    ProfileHandleListElm_t *elem = NULL;
    VcpProfileHandle profile_hndl = 0;
    uint8 i;

    if (appTask == CSR_SCHED_QID_INVALID)
    {
        VCP_PANIC("Application Task NULL\n");
    }

    elem = VCP_ADD_SERVICE_HANDLE(vcp_main->profile_handle_list);
    profile_hndl = ADD_VCP_CLIENT_INST(vcp_inst);
    elem->profile_handle = profile_hndl;

    if (vcp_inst)
    {
        /* Reset all the service library memory */
        memset(vcp_inst, 0, sizeof(VCP));

        /* Register with GATT to get GATT messages */
        vcp_inst->gattId = CsrBtGattRegister(CSR_BT_VCP_IFACEQUEUE);

#ifndef EXCLUDE_CSR_BT_VOCS_CLIENT_MODULE
        CsrCmnListInit(&vcp_inst->vocs_service_queue, 0, vocsInitQueueInst, vocsDeInitQueueInst);
#endif

#ifndef EXCLUDE_CSR_BT_AICS_CLIENT_MODULE
        CsrCmnListInit(&vcp_inst->aics_service_queue, 0, aicsInitQueueInst, aicsDeInitQueueInst);
#endif

        /* Set up library handler for external messages */
        vcp_inst->lib_task = CSR_BT_VCP_IFACEQUEUE;

        /* Store the Task function parameter.
         * All library messages need to be sent here */
        vcp_inst->app_task = appTask;

        vcp_inst->cid = clientInitParams->cid;

        vcp_inst->vcp_srvc_hdl = profile_hndl;

        vcp_inst->secondary_service_req = includedServices;

        vcpSendInitCfm(vcp_inst, VCP_STATUS_IN_PROGRESS);

        if(deviceData)
        {
            /* It's a peer device: we already know handles, no need to do discovery */
            vcp_inst->is_peer_device = TRUE;

            vcp_inst->start_handle = deviceData->vcsHandle.startHandle;
            vcp_inst->end_handle = deviceData->vcsHandle.endHandle;

            vcp_inst->vocs_counter = deviceData->vocsHandleLength;
            vcp_inst->aics_counter = deviceData->aicsHandleLength;

            /* Check if the application requested the VCP Secondary service,
             * if so after the initialisation of the VCS Client
             * the  VOCS and/or AICS Clients must also be initialised.
             */
            if (deviceData->vocsHandle)
            {
                /* There are VOCS Client to initialise: we can't initialise them now, but we have to wait
                 * the confirmation of the VCS Client initialisation. We cache the data provided by the app
                 * in a dynamic list. We use a dynamic list instead of an array, even if we know the number of
                 * VOCS instances we have to inialise, because in the case in which the remote device is not
                 * a peer device we have to discover its secondary services, we don't know how many of them we
                 * will find until we finish the discovery. In that case we need a dynamic list to save the
                 * discovery data. In order to not have too many data structure (array and list) with the same
                 * meaning, we chose to use always the list.
                 */
                vcp_inst->vocs_counter = deviceData->vocsHandleLength;

                for (i = 0; i < deviceData->vocsHandleLength; i++)
                {
                     vcpVocsServiceQueueElement *incElement = VCP_VOCS_SERVICE_QUEUE_ADD_LAST(vcp_inst->vocs_service_queue);

                     if (incElement)
                     {
                         incElement->device_data.startHandle = deviceData->vocsHandle[i].startHandle;
                         incElement->device_data.endHandle = deviceData->vocsHandle[i].endHandle;
                         memcpy(&(incElement->device_data), &deviceData->vocsHandle[i].handles, sizeof(GattVocsClientDeviceData));
                     }
                }
            }

            if (deviceData->aicsHandle)
            {
                /* Same thing for the AICS instances*/
                vcp_inst->aics_counter = deviceData->aicsHandleLength;

                for (i = 0; i < deviceData->aicsHandleLength; i++)
                {
                      vcpAicsServiceQueueElement *incElement = VCP_AICS_SERVICE_QUEUE_ADD_LAST(vcp_inst->aics_service_queue);

                      if (incElement)
                      {
                          incElement->device_data.startHandle = deviceData->aicsHandle[i].startHandle;
                          incElement->device_data.startHandle = deviceData->aicsHandle[i].endHandle;
                          memcpy(&(incElement->device_data), &deviceData->aicsHandle[i].handles, sizeof(GattAicsClientDeviceData));
                      }
                }
            }

            /* We can start now the initialisation of all the necessary client:
             * we start with the VCS Client.
             */
            vcpVcsInitReq(vcp_inst, clientInitParams, deviceData);
        }
        else
        {
            GattSdSrvcId srvcIds = GATT_SD_VCS_SRVC;
            /* Find handle value range for the VCP from GATT SD */
            GattServiceDiscoveryFindServiceRange(CSR_BT_VCP_IFACEQUEUE, vcp_inst->cid, srvcIds);
        }
    }
    else
    {
        VCP_PANIC("Memory alllocation of VCP Profile instance failed!\n");
    }
}

#ifndef EXCLUDE_CSR_BT_AICS_CLIENT_MODULE
/****************************************************************************/
void vcpHandleAicsClientInitResp(VCP *vcp_inst,
                                 const GattAicsClientInitCfm *message)
{
    if(vcp_inst)
    {
        if(message->status == GATT_AICS_CLIENT_STATUS_SUCCESS)
        {
            /* The initialisation of a VOCS client instance is done */
            vcp_inst->aics_counter -= 1;

            CsrCmnListSearch(&vcp_inst->aics_service_queue, setAicsServiceHandle, (void *)message);

            /* Check all the included services are disocvered or not */
            if (vcp_inst->vocs_counter == 0 && vcp_inst->aics_counter == 0)
            {
                 vcpSendInitOptServiceCfm(vcp_inst, VCP_STATUS_SUCCESS);
            }
        }
        else
        {
            /* One of the AICS Client Initialisation failed */
            /* Check all the other included services are disocvered or not */
            if (vcp_inst->vocs_counter == 0)
            {
                 /* Remove the mask for the failure service */
                 vcp_inst->opt_service = vcp_inst->opt_service & (~VCP_INCLUDE_AICS);

                 vcpSendInitOptServiceCfm(vcp_inst, VCP_STATUS_SUCCESS);
            }
            else
            {
                 VCP_INFO("vcpHandleAicsClientInitResp vcp_inst->vocs_counter : %x  vcp_inst->aics_counter : 0x%x", vcp_inst->vocs_counter,
                    vcp_inst->aics_counter);
            }
        }
    }
    else
    {
        VCP_DEBUG("Invalid VCP Profile instance\n");
    }
}
#endif

#ifndef EXCLUDE_CSR_BT_VOCS_CLIENT_MODULE
/****************************************************************************/
void vcpHandleVocsClientInitResp(VCP *vcp_inst,
                                 const GattVocsClientInitCfm *message)
{
    if(vcp_inst)
    {
        if(message->status == GATT_VOCS_CLIENT_STATUS_SUCCESS)
        {
            /* The initialisation of a VOCS client instance is done */
            vcp_inst->vocs_counter -= 1;

            CsrCmnListSearch(&vcp_inst->vocs_service_queue, setVocsServiceHandle, (void*) message);

            /* Check all the included services are disocvered or not */
            if (vcp_inst->vocs_counter == 0 && vcp_inst->aics_counter == 0)
            {
                 vcpSendInitOptServiceCfm(vcp_inst, VCP_STATUS_SUCCESS);
            }
        }
        else
        {
            /* One of the VOCS Client Initialisation failed */
            /* Check all the other included services are disocvered or not */
            if (vcp_inst->aics_counter == 0)
            {
                 /* Remove the mask for the failure service */
                 vcp_inst->opt_service = vcp_inst->opt_service & (~VCP_INCLUDE_VOCS);

                 vcpSendInitOptServiceCfm(vcp_inst, VCP_STATUS_SUCCESS);
            }
            else
            {
                 VCP_INFO("vcpHandleVocsClientInitResp vcp_inst->vocs_counter : %x  vcp_inst->aics_counter : 0x%x", vcp_inst->vocs_counter,
                    vcp_inst->aics_counter);
            }
        }
    }
    else
    {
        VCP_DEBUG("Invalid VCP Profile instance\n");
    }
}
#endif

/****************************************************************************/
void vcpHandleVcsClientInitResp(VCP *vcp_inst,
                                const GattVcsClientInitCfm *message)
{
    if(vcp_inst)
    {
        if(message->status == GATT_VCS_CLIENT_STATUS_SUCCESS)
        {
            vcp_inst->vcs_srvc_hdl = message->srvcHndl;

             /* Application is intrested for included services and included services handles are not given by the Application */
            if (vcp_inst->secondary_service_req && !vcp_inst->is_peer_device)
            {
                CsrBtGattFindInclServicesReqSend(vcp_inst->gattId,
                                            vcp_inst->cid,
                                            vcp_inst->start_handle,
                                            vcp_inst->end_handle);
            }
            else
            {
                /* The applicantion didn't request the discovery of the secondary service:
                 * we can send the confirmation of the initialisation
                 */
                vcpSendInitCfm(vcp_inst, VCP_STATUS_SUCCESS);
            }
        }
        else
        {
            /* The initialisation of VCS Client failed:
             * we need to destroy all the existed instance lists.*/

            vcpSendInitCfm(vcp_inst, VCP_STATUS_FAILED);
        }
    }
    else
    {
        VCP_DEBUG("Invalid VCP Profile instance\n");
    }
}

/*************************************************************************************/
void VcpInitOptServiceReq(VcpProfileHandle profileHandle, vcpIncOptService  optService)
{
    VCP *vcp_inst = ServiceHandleGetInstanceData((ServiceHandle) profileHandle);
    vcpIncOptService incService;

    if (vcp_inst)
    {
        if (optService == 0x00)
        {
            MAKE_VCP_MESSAGE(VcpInitOptServiceCfm);

            message->id = VCP_INIT_OPTIONAL_SERVICE_CFM;
            message->status = VCP_STATUS_INVALID_PARAMETER;
            message->prflHndl = vcp_inst->vcp_srvc_hdl;
            message->optSerFound = optService;

            VcpMessageSend(vcp_inst->app_task, message);
            return;
        }
#if (!defined EXCLUDE_CSR_BT_AICS_CLIENT_MODULE || !defined EXCLUDE_CSR_BT_VOCS_CLIENT_MODULE)
        else if (vcp_inst->vocs_service_queue.count == 0
            && vcp_inst->aics_service_queue.count == 0)
        {
            MAKE_VCP_MESSAGE(VcpInitOptServiceCfm);
            
            message->id = VCP_INIT_OPTIONAL_SERVICE_CFM;
            message->status = VCP_STATUS_INVALID_PARAMETER;
            message->prflHndl = vcp_inst->vcp_srvc_hdl;
            message->optSerFound = optService;
            
            VcpMessageSend(vcp_inst->app_task, message);
            return;
        }
#endif
        vcp_inst->opt_service = optService;

        if ((optService & VCP_INCLUDE_VOCS) == VCP_INCLUDE_VOCS)
        {
            incService = VCP_INCLUDE_VOCS;
            CsrCmnListIterate(&vcp_inst->vocs_service_queue, initiateIncService, &incService);

            optService = optService& ~VCP_INCLUDE_VOCS;
        }

        if ((optService & VCP_INCLUDE_AICS) == VCP_INCLUDE_AICS)
        {
            incService = VCP_INCLUDE_AICS;
            CsrCmnListIterate(&vcp_inst->aics_service_queue, initiateIncService, &incService);

            optService = optService& ~VCP_INCLUDE_AICS;
        }
    }
    else
    {
        VCP_DEBUG("Invalid profile_handle\n");
    }
}

/***************************************************************************/
void VcpSetInitialVolReq(VcpProfileHandle profileHandle, uint8 initialVol)
{
    VCP *vcp_inst = ServiceHandleGetInstanceData((ServiceHandle) profileHandle);

    if (vcp_inst)
    {
        MAKE_VCP_INTERNAL_MESSAGE(VCP_INTERNAL_SET_INITIAL_VOL_OP);

        message->id = VCP_INTERNAL_SET_INITIAL_VOL_OP;
        message->prfl_hndl = profileHandle;
        message->initial_vol = initialVol;

        VcpMessageSendConditionally(vcp_inst->lib_task,
                                    VCP_INTERNAL_SET_INITIAL_VOL_OP,
                                    message,
                                    &vcp_inst->pending_op);
    }
    else
    {
        VCP_DEBUG("Invalid VCP profile instance\n");
    }
}

/***************************************************************************/
void vcpSendSetInitialVolOpCfm(VCP *vcp_inst,
                               VcpStatus status)
{
    MAKE_VCP_MESSAGE(VcpSetInitialVolCfm);

    message->id = VCP_SET_INITIAL_VOL_CFM;
    message->prflHndl = vcp_inst->vcp_srvc_hdl;
    message->status = status;

    VcpMessageSend(vcp_inst->app_task, message);
}

/***************************************************************************/
vcp_main_inst *vcpGetMainInstance(void)
{
    return vcp_main;
}

/****************************************************************************/
#ifdef ENABLE_SHUTDOWN
void vcp_deinit(void **gash)
{
    CsrCmnListDeinit(&vcp_main->profile_handle_list);
    CsrPmemFree(vcp_main);
}
#endif
